/*
 * Copyright (c) 2020, yifengling0. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. No personal names or organizations' names associated with the
 *    Atomthreads project may be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include "simSpi.h"
#include "pin.h"
#include "hw.h"

static int sim_spi_xfer_unit(SimSpi *dev, int val)
{
	int result = 0;
	int i;

    CRITICAL_STORE;

    CRITICAL_START ();

	if (dev->Super.Protected.mode & SPI_LSB_FIRST)
	{
		for (i = 0; i < dev->Super.Protected.bitWidth; i++)
		{
			PinWrite(dev->Protected.ctrl->scl, LOW);
			PinWrite(dev->Protected.ctrl->mosi, ((val & (1 << i)) ? HIGH : LOW));
			HwDelayUs(dev->Protected.ctrl->delay);
			PinWrite(dev->Protected.ctrl->scl, HIGH);    // CPHA=1，在时钟的第一个跳变沿采样
            if( dev->Protected.ctrl->miso != -1){
                result |= (PinRead(dev->Protected.ctrl->miso) ? 1 : 0) << i;
            }
			HwDelayUs(dev->Protected.ctrl->delay);
		}
	}
	else
	{
		for (i = dev->Super.Protected.bitWidth - 1; i >= 0; i--)
		{
            PinWrite(dev->Protected.ctrl->scl, LOW);
			PinWrite(dev->Protected.ctrl->mosi, ((val & (1 << i)) ? HIGH : LOW));
			HwDelayUs(dev->Protected.ctrl->delay);
			PinWrite(dev->Protected.ctrl->scl, HIGH);    // CPHA=1，在时钟的第一个跳变沿采样
            if( dev->Protected.ctrl->miso != -1){
                result |= (PinRead(dev->Protected.ctrl->miso) ? 1 : 0) << i;
            }
			HwDelayUs(dev->Protected.ctrl->delay);
		}
	}

	CRITICAL_END();

	return result;
}

static int sim_spi_xfer(SpiBase *dev, const void *send_buf, void *recv_buf, size_t length)
{
	SimSpi *pDrv = (SimSpi *)dev;
    
    if(pDrv->Protected.ctrl->autoCs){
        PinWrite(pDrv->Protected.ctrl->cs, LOW);    
    }

	if (send_buf && recv_buf)
	{
		uint8_t *txBuf = (uint8_t *)send_buf;
		uint8_t *rxBuf = (uint8_t *)recv_buf;
		int bufLen = length;
		while (bufLen-- > 0)
		{
			*rxBuf++ = pDrv->Protected.xfer_unit(pDrv, *txBuf++);
		}
	}
	else if (recv_buf)
	{
		uint8_t *rxBuf = (uint8_t *)recv_buf;
		int bufLen = length;
		while (bufLen-- > 0)
		{
			*rxBuf++ = pDrv->Protected.xfer_unit(pDrv, 0xff);
		}
	}
	else if (send_buf)
	{
		uint8_t *txBuf = (uint8_t *)send_buf;
		int bufLen = length;
		while (bufLen-- > 0)
		{
			pDrv->Protected.xfer_unit(pDrv, *txBuf++);
		}
	}
    
    if(pDrv->Protected.ctrl->autoCs){
        PinWrite(pDrv->Protected.ctrl->cs, HIGH);
    }

	return 0;
}

static void  sim_spi_mode_cfg(SimSpi* dev, SimSpiDevCtrl* ctrl)
{
    PinMode(dev->Protected.ctrl->cs, PIN_MODE_OUTPUT);
    PinMode(dev->Protected.ctrl->scl, PIN_MODE_OUTPUT);
    PinMode(dev->Protected.ctrl->mosi, PIN_MODE_OUTPUT);
    PinMode(dev->Protected.ctrl->miso, PIN_MODE_OUTPUT);
    
	dev->Protected.ctrl->delay = (((uint32_t)1000 * 1000) /dev->Super.Protected.freq)/10;
	dev->Protected.ctrl->delay = MAX(1, dev->Protected.ctrl->delay);
}

void SimSpiCreate(SimSpi* dev, const char *name,  int mode, int dataLines, int bitwidth , uint32_t freq, SimSpiDevCtrl* ctrl, FSimSpiXferUnit xfer_unit)
{
    dev->Protected.ctrl = ctrl;
    // 外部不设定xfer函数情况下，使用默认的收发处理，暂时只对应了mode0
    SpiCreate(&dev->Super, name, mode, 0, 0, dataLines, bitwidth, freq, sim_spi_xfer);

    if( xfer_unit != NULL ){
        dev->Protected.xfer_unit = xfer_unit;
    }else{
        dev->Protected.xfer_unit = sim_spi_xfer_unit;
    }

    sim_spi_mode_cfg(dev, ctrl);
}